<template>
  <div :class="['bflogin',locale]" :style="bgStyle">
    <div class="login-modal">
      <h3 class="login-modal-title" v-text="$t('login.title')"></h3>
      <el-form :model="loginForm"
               statusIcon :rules="loginRules"
               ref="loginForm">
        <el-form-item>
          <el-select v-model="locale"
                     @change="setSystemLang"
                     :placeholder="$t('login.useLang')">
            <el-option
              v-for="item in locales"
              :key="item.value"
              :label="item.label"
              :value="item.value">
            </el-option>
          </el-select>
        </el-form-item>
        <el-form-item prop="username">
          <el-autocomplete
            class="el-input inline-input"
            :fetch-suggestions="querySearch"
            :trigger-on-focus="false"
            v-model="loginForm.username"
            :placeholder="$t('login.username')"
            prefix-icon="bf-icon gw-login-user"
            @select="handleSelect"
            @keydown.native.enter="inputEnterLogin">
          </el-autocomplete>
        </el-form-item>
        <el-form-item prop="password">
          <el-input v-model="loginForm.password"
                    auto-complete="off" type="password"
                    :placeholder="$t('login.password')"
                    prefix-icon="bf-icon gw-password"
                    @keydown.native.enter="inputEnterLogin"></el-input>
        </el-form-item>
        <el-form-item prop="remember"
                      class="login-modal-remember">
          <el-checkbox v-model="remember">
            <span v-text="$t('login.remember')"></span>
          </el-checkbox>
          <div class="login-modal-notify">
            <span v-if="loginStats=='loading'"
                  v-text="$t('login.loading')"></span>
            <span v-else-if="loginStats=='connecting'"
                  v-text="$t('login.connecting')"></span>
            <span v-else-if="loginStats=='connected'"
                  v-text="$t('login.connected')"></span>
            <span v-else-if="loginStats=='disconnect'"
                  v-text="$t('login.disconnect')"></span>
            <span v-else-if="loginStats=='passwordError'"
                  v-text="$t('login.passwordError')"></span>
            <span v-else-if="loginStats=='oldSession'"
                  v-text="$t('login.oldSession')"></span>
            <span v-else-if="loginStats=='noHasUser'"
                  v-text="$t('login.withoutTheUser')"></span>
            <span v-else-if="loginStats=='loginTimeout'"
                  v-text="$t('login.loginTimeout')"></span>
            <span v-else="loginStats=='receiving'"
                  v-text="$t('login.logining')"></span>
          </div>
        </el-form-item>
        <el-form-item class="login-modal-button">
          <el-button type="primary" v-text="$t('login.login')"
                     :disabled="disSignIn"
                     @click="inputEnterLogin"></el-button>
        </el-form-item>
      </el-form>
    </div>
  </div>
</template>

<script lang="ts">
  import {Vue, Component, Prop} from 'vue-property-decorator'
  import {State, Mutation} from 'vuex-class'
  import * as _ from 'lodash'
  import base64 from 'base64-js'

  import bfI18n,{loadLanguageAsync} from '../modules/i18n'
  import bfProto from '../modules/proto'
  import bfStorage from '../utils/bf-storage'
  import bfConsole from '../utils/bf-console'
  import bfRules from '../utils/bf-rules'
  import bfCrypto from '../utils/bf-crypto'
  import bfEvent from '../utils/bf-event'

  @Component
  export default class bfLogin extends Vue {
    // data
    loginForm: loginForm = {
      username: '',
      password: '',
      sessionId: ''
    };
    remember: boolean = true;
    disSignIn: boolean = true;
    loginStats: string = "loading";
    locale: string = bfI18n.locale;
    accounts: any = [];

    // methods
    setSystemLang(lang) {
      loadLanguageAsync(lang)
    }

    removeExistAccount(username) {
      for (let i = 0; i < this.accounts.length; i++) {
        let item: account = this.accounts[i];
        if (item.value == username) {
          this.accounts.splice(i, 1);
          return
        }
      }
    }

    // 记住用户账号名,登录成功后执行
    remembered() {
      // 因需要将当前登录账号信息缓存为最后一个，故需要先删除旧的登录的数据
      this.removeExistAccount(this.loginForm.username);
      if (this.remember == true) {
        // 将当前登录的账号信息缓存的accounts中最后一个
        // element-ui autocomplete 必须指定value值
        let account: account = {
          sessionId: this.loginForm.sessionId,
          password: _.padStart("*", this.loginForm.password.length, "*"),
          locale: bfI18n.locale,
          value: this.loginForm.username
        };
        this.accounts.push(account);
      }
      // 因已删除旧的登录数据，只要保存删除后的即可
      bfStorage.setStorageAccounts(JSON.stringify(this.accounts));
    }

    // 登录响应,解析登录命令
    resLoginFunc(rpc_cmd_obj: any) {
      let body = rpc_cmd_obj.body;
      switch (body.resCode) {
        case 1:
          this.loginStats = 'noHasUser';
          break;
        case 2:
          this.loginStats = 'passwordError';
          break;
        case 3:
          this.loginStats = 'oldSession';
          this.resetSession();
          break;
        case 100:
          // 将登录响应数据和登录表单数据发送到处理登录响应方法中
          let user = bfProto.decode(rpc_cmd_obj.paraBin, "db_user");
          this.isLogin(true);
          this.remembered();
          this.userInfo.ssid = body.sessionId;
          this.userInfo.originData = user;
          this.userInfo.setting = JSON.parse(user.setting);
          this.saveUserInfo(this.userInfo);

          // 添加登录成功日志
          bfEvent.once("bflogLoaded", () => {
            bfEvent.emit("addLog", "logs.loginSuccess");
          });
          break;
        case 0:
        default:
          bfConsole.warn("unkown res!", rpc_cmd_obj);
          break;
      }
    }

    encode_userName(userName: string) {
      // 加密步骤：base64(xor(orig_user_name,str(server_time)))
      if (!userName) {
        return ""
      }
      let _key = bfCrypto.toUTF8Array("" + this.serverInfo.serverTime);
      let _data = bfCrypto.toUTF8Array(userName);
      let xorBytes = bfCrypto.bfXor(_data, _key);

      return base64.fromByteArray(xorBytes);
    }

    encode_password(userName: string, password: string) {
      // 加密步骤：user_passwd=base64(xor(sha256(orig_user_name+orig_user_passwd),str(server_time))
      if (!userName || !password) {
        return ""
      }
      let sha_str = bfCrypto.sha256(userName + password, "ARRAYBUFFER");
      sha_str = new Uint8Array(sha_str);
      let key = bfCrypto.toUTF8Array("" + this.serverInfo.serverTime);
      let xorBytes = bfCrypto.bfXor(sha_str, key);

      return base64.fromByteArray(xorBytes);
    }

    // 登录
    loginNow() {
      this.loginStats = "receiving";

      let message = {
        userName: this.encode_userName(this.loginForm.username),
        userPasswd: this.encode_password(this.loginForm.username, this.loginForm.password),
        userType: 60,
        sessionId: this.loginForm.sessionId || 0,
        clientVersion: "1",
        clientProtoVersion: 1,
        clientPlatformInfo: window.navigator.userAgent,
        clientHardwareSn: "",
        clientLocale: this.locale,
        loginType: /^[*]+$/.test(this.loginForm.password) ? 1 : 0,
        timeout: 30,
        locale: bfI18n.locale
      };
      let opts: socketSendOption = {
        decodeMsgType: "client_login_res"
      };
      bfProto.sendMessage(1, message, "client_login", opts).then(this.resLoginFunc).catch(err => {
        this.loginStats = "loginTimeout";
        bfConsole.error(err)
      });
    }

    // 回车登录
    inputEnterLogin() {
      if (this.disSignIn) {
        return
      }
      let loginForm: any = this.$refs['loginForm'];
      loginForm.validate((valid) => {
        if (valid) {
          this.loginNow();
        } else {
          this.resetSession();
          return false
        }
      });
    }

    // 重置sessionId
    resetSession() {
      this.loginForm.password = "";
      this.loginForm.sessionId = "";
    }

    // websocket连接成功
    serverConnected() {
      this.loginStats = "connected";
      this.disSignIn = false;
    }

    // websocket连接失败
    serverDiconnect() {
      this.loginStats = "disconnect";
      this.disSignIn = true;
    }

    // 用户名自动输入提示方法
    handleSelect(item) {
      this.loginForm.password = item.password;
      this.locale = bfI18n.locale = item.locale;
    }

    querySearch(queryString, cb) {
      let filterFunc = this.createFilter(queryString);
      let results = queryString ? this.accounts.filter(filterFunc) : this.accounts;
      // 调用 callback 返回建议列表的数据
      cb(results);
    }

    createFilter(queryString) {
      return (account) => {
        return account.value.toLowerCase().includes(queryString.toLowerCase());
      };
    }

    // 读取系统缓存账号信息
    readStorageAccount() {
      let accounts: any = bfStorage.getStorageAccounts();
      if (accounts && accounts.length > 0) {
        this.accounts = Object.assign([], accounts);
        // 自动填写最后一个记录到登录表单中
        let account = accounts.pop();
        this.loginForm.sessionId = account.sessionId;
        this.loginForm.password = account.password;
        this.loginForm.username = account.value;
        loadLanguageAsync(account.locale).then(lang=>{
          this.locale = bfI18n.locale = lang;
        });
      }
    }

    // mounted
    mounted() {
      this.readStorageAccount();

      bfEvent.on("serverConnected", this.serverConnected);
      bfEvent.on("serverDiconnect", this.serverDiconnect);
      bfEvent.once("serverConnecting", () => {
        this.loginStats = "connected";
      });
      bfProto.connect();
    }

    // computed
    get loginRules() {
      return {
        username: [
          bfRules.required(),
          bfRules.inputLengthLimit(16)
        ],
        password: [
          bfRules.required(),
          bfRules.inputLengthLimit(16)
        ]
      }
    }

    get locales() {
      return [
        {label: bfI18n.t("header.zh-CN"), value: "zh-CN"},
        {label: bfI18n.t("header.en"), value: "en"}
      ];
    }

    get bgStyle() {
      return this.bgIcon ? {
        'background': `#f9f9f9 url(${this.bgIcon}) no-repeat center`
      } : ''
    }

    // Props
    @Prop({default: ""})
    bgIcon: string;

    // store
    @Mutation isLogin;
    @Mutation saveUserInfo;

    @State serverInfo;
    @State userInfo;

  }

</script>

<style lang="scss">
  .bflogin.zh-CN {
    background: #f9f9f9 url("../assets/bg_cn.jpg") no-repeat center;
  }

  .bflogin.en {
    background: #f9f9f9 url("../assets/bg_en.jpg") no-repeat center;
  }

  .bflogin {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 1010;
    background-size: cover !important;

    .login-modal {
      background: linear-gradient(rgba(0, 0, 0, .4), rgba(255, 255, 255, 0.05));
      padding: 15px 30px;
      position: absolute;
      top: 50%;
      left: 50%;
      transform: translateX(-50%) translateY(-50%);
      border-radius: 5px;
      box-sizing: border-box;
      color: #fff;

      .login-modal-title {
        line-height: 32px;
        margin-bottom: 10px;
        font-size: 1.4em;
        text-align: center;
      }

      .el-form {
        .el-form-item {
          .el-select {
            width: 100%;
          }
          .el-form-item__error {
            top: 50%;
            left: unset;
            right: 30px;
            transform: translate(0, -50%);
          }
        }
        .el-form-item:not(:last-child) {
          margin-bottom: 10px;
        }

        .login-modal-remember {
          margin-bottom: 0;
          text-align: left;
        }
        .login-modal-notify, .login-modal-button {
          margin-bottom: 0;
        }

        .login-modal-notify {
          float: right;
          color: #F56C6C;
          font-size: 1.2em;
        }

        .login-modal-button {
          text-align: center;

          button {
            width: 100%;
            min-width: 120px;
          }
        }
      }
    }

  }

  @media (min-width: 2560px) {
    .bflogin .login-modal {
      width: 20%;
    }
  }

  @media (max-width: 2559px) {
    .bflogin .login-modal {
      width: 25%;
    }
  }

  @media (max-width: 1919px) {
    .bflogin .login-modal {
      width: 32%;
    }
  }

  @media (max-width: 1199px) {
    .bflogin .login-modal {
      width: 45%;
    }
  }

  @media (max-width: 991px) {
    .bflogin .login-modal {
      width: 65%;
    }
  }

  @media (max-width: 767px) {
    .bflogin .login-modal {
      width: 90%;
    }
  }

  @media (max-width: 424px) {
    .bflogin .login-modal {
      width: 95%;
    }
  }

</style>
